package com.yifeng.repo.base.utils.redis.worker;

import com.yifeng.repo.base.utils.converter.JacksonHelper;
import com.yifeng.repo.base.utils.redis.client.AbstractJedisCommand;
import com.yifeng.repo.base.utils.redis.client.JedisClient;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;

import java.util.*;

/**
 * Created by daibing on 2022/8/9.
 */
public class RedisCacheWorker {
    private final JedisClient jedisClient;

    public RedisCacheWorker(JedisClient jedisClient) {
        this.jedisClient = jedisClient;
    }

    public boolean containsKey(String key) {
        return new AbstractJedisCommand<Boolean>() {
            @Override
            protected Boolean doRun(Jedis jedis) {
                return jedis.exists(key);
            }
        }.doExecute(jedisClient);
    }

    public void put(String key, String value) {
        // 默认设置 ttl 3分钟
        put(key, 3 * 60, value);
    }

    public void put(String key, long seconds, Object value) {
        put(key, seconds, JacksonHelper.toJson(value));
    }

    public void put(String key, long seconds, String value) {
        new AbstractJedisCommand<String>() {
            @Override
            protected String doRun(Jedis jedis) {
                if (seconds > 0) {
                    return jedis.setex(key, seconds, value);
                } else {
                    return jedis.set(key, value);
                }
            }
        }.doExecute(jedisClient);
    }

    public String get(String key) {
        return new AbstractJedisCommand<String>() {
            @Override
            protected String doRun(Jedis jedis) {
                return jedis.get(key);
            }
        }.doExecute(jedisClient);
    }

    public void remove(String key) {
        new AbstractJedisCommand<Long>() {
            @Override
            protected Long doRun(Jedis jedis) {
                return jedis.del(key);
            }
        }.doExecute(jedisClient);
    }

    public void expire(String key, long millis) {
        new AbstractJedisCommand<Long>() {
            @Override
            protected Long doRun(Jedis jedis) {
                return jedis.pexpire(key, millis);
            }
        }.doExecute(jedisClient);
    }

    public Long incrBy(String key, long value) {
        return new AbstractJedisCommand<Long>() {
            @Override
            protected Long doRun(Jedis jedis) {
                return jedis.incrBy(key, value);
            }
        }.doExecute(jedisClient);
    }

    public Long decrBy(String key, long value) {
        return new AbstractJedisCommand<Long>() {
            @Override
            protected Long doRun(Jedis jedis) {
                return jedis.decrBy(key, value);
            }
        }.doExecute(jedisClient);
    }

    public List<String> scan(String keyPattern, int count, int times) {
        ScanParams scanParams = new ScanParams().count(count).match(keyPattern);
        return new AbstractJedisCommand<List<String>>() {
            @Override
            protected List<String> doRun(Jedis jedis) {
                List<String> keys = new ArrayList<>();
                String cursor = ScanParams.SCAN_POINTER_START;
                for (int i = 0; i < times; i++) {
                    ScanResult<String> scan = jedis.scan(cursor, scanParams);
                    keys.addAll(scan.getResult());
                    if (ScanParams.SCAN_POINTER_START.equals(scan.getCursor())) {
                        break;
                    }
                    cursor = scan.getCursor();
                }
                return keys;
            }
        }.doExecute(jedisClient);
    }

    /**
     * Hash
     */

    public void hashPut(String key, Map<String, String> map) {
        if (map == null || map.isEmpty()) {
            return;
        }
        new AbstractJedisCommand<String>() {
            @Override
            protected String doRun(Jedis jedis) {
                return jedis.hmset(key, map);
            }
        }.doExecute(jedisClient);
    }

    public void hashPut(String key, long seconds, Map<String, String> map) {
        if (map == null || map.isEmpty()) {
            return;
        }
        new AbstractJedisCommand<Long>() {
            @Override
            protected Long doRun(Jedis jedis) {
                jedis.hmset(key, map);
                return jedis.expire(key, seconds);
            }
        }.doExecute(jedisClient);
    }

    public void hashPut(String key, long seconds, String field, String value) {
        new AbstractJedisCommand<Long>() {
            @Override
            protected Long doRun(Jedis jedis) {
                jedis.hset(key, field, value);
                return jedis.expire(key, seconds);
            }
        }.doExecute(jedisClient);
    }

    public void hashPutByPipeline(Map<String, Map<String, String>> key2FieldValues, long seconds) {
        new AbstractJedisCommand<Long>() {
            @Override
            protected Long doRun(Jedis jedis) {
                Pipeline pipeline = jedis.pipelined();
                key2FieldValues.forEach((k, v) -> {
                    pipeline.hmset(k, v);
                    pipeline.expire(k, seconds);
                });
                pipeline.sync();
                return 0L;
            }
        }.doExecute(jedisClient);
    }

    public void hashPut(String key, String field, String value) {
        new AbstractJedisCommand<Long>() {
            @Override
            protected Long doRun(Jedis jedis) {
                return jedis.hset(key, field, value);
            }
        }.doExecute(jedisClient);
    }

    /**
     * 【谨慎使用】
     * 注意：在元素数量未知的情况下，一定不要无脑执行hgetall、lrange、smembers、zrange、sinter这些命令都是做全集操作，如果元素很多，会消耗大量CPU资源。
     * 可使用hscan、sscan、zscan这些分批扫描的命令替代。
     */
    public Map<String, String> hashGet(String key) {
        return new AbstractJedisCommand<Map<String, String>>() {
            @Override
            protected Map<String, String> doRun(Jedis jedis) {
                return jedis.hgetAll(key);
            }
        }.doExecute(jedisClient);
    }

    public String hashGet(String key, String field) {
        return new AbstractJedisCommand<String>() {
            @Override
            protected String doRun(Jedis jedis) {
                return jedis.hget(key, field);
            }
        }.doExecute(jedisClient);
    }

    public Map<String, String> hashGet(String key, String[] fields) {
        List<String> values = new AbstractJedisCommand<List<String>>() {
            @Override
            protected List<String> doRun(Jedis jedis) {
                return jedis.hmget(key, fields);
            }
        }.doExecute(jedisClient);
        if (fields.length != values.size()) {
            throw new RuntimeException("fields.length is not equal to values.size");
        }
        Map<String, String> map = new HashMap<>(fields.length);
        for (int i = 0; i < fields.length; i++) {
            map.put(fields[i], values.get(i));
        }
        return map;
    }

    public List<String> hashValueSet(String key) {
        return new AbstractJedisCommand<List<String>>() {
            @Override
            protected List<String> doRun(Jedis jedis) {
                return jedis.hvals(key);
            }
        }.doExecute(jedisClient);
    }

    /**
     * 注意：【谨慎使用】代码侧禁止使用keys命令获取key，使用 SCAN 来代替，分批返回符合条件的键值对，避免主线程阻塞
     */
    public Set<String> hashFieldSet(String key) {
        return new AbstractJedisCommand<Set<String>>() {
            @Override
            protected Set<String> doRun(Jedis jedis) {
                return jedis.hkeys(key);
            }
        }.doExecute(jedisClient);
    }

    public Long hashRemoveField(String key, String[] fields) {
        return new AbstractJedisCommand<Long>() {
            @Override
            protected Long doRun(Jedis jedis) {
                return jedis.hdel(key, fields);
            }
        }.doExecute(jedisClient);
    }

    public void hashRemoveFieldByPipeline(Map<String, List<String>> key2Fields) {
        new AbstractJedisCommand<Long>() {
            @Override
            protected Long doRun(Jedis jedis) {
                Pipeline pipeline = jedis.pipelined();
                key2Fields.forEach((k, v) -> pipeline.hdel(k, v.toArray(new String[0])));
                pipeline.sync();
                return 0L;
            }
        }.doExecute(jedisClient);
    }

    public long hashLength(String key) {
        return new AbstractJedisCommand<Long>() {
            @Override
            protected Long doRun(Jedis jedis) {
                return jedis.hlen(key);
            }
        }.doExecute(jedisClient);
    }

    public Long hashIncrBy(String key, String field, long value) {
        return new AbstractJedisCommand<Long>() {
            @Override
            protected Long doRun(Jedis jedis) {
                return jedis.hincrBy(key, field, value);
            }
        }.doExecute(jedisClient);
    }

    /**
     * List队列
     */

    /**
     * 队列左边插入数据
     */
    public void lpush(String key, String... elements) {
        new AbstractJedisCommand<Long>() {
            @Override
            protected Long doRun(Jedis jedis) {
                return jedis.lpush(key, elements);
            }
        }.doExecute(jedisClient);
    }

    public void lpush(String key, long seconds, String... elements) {
        new AbstractJedisCommand<Long>() {
            @Override
            protected Long doRun(Jedis jedis) {
                jedis.lpush(key, elements);
                return jedis.expire(key, seconds);
            }
        }.doExecute(jedisClient);
    }

    /**
     * 队列右边取出数据
     */
    public String rpop(String key) {
        return new AbstractJedisCommand<String>() {
            @Override
            protected String doRun(Jedis jedis) {
                return jedis.rpop(key);
            }
        }.doExecute(jedisClient);
    }

    /**
     * 注意：【谨慎使用】查询整个队列数据
     */
    public List<String> lrange(String key) {
        return new AbstractJedisCommand<List<String>>() {
            @Override
            protected List<String> doRun(Jedis jedis) {
                return jedis.lrange(key, 0, -1);
            }
        }.doExecute(jedisClient);
    }

}
